多线程
线程的状态
- NEW 新建状态: new 一个线程
- RUNNABLE 运行状态: 获取 CPU 时间片之后, 开始 run() 方法之后,开始执行
- BLOCKED 阻塞状态: 被阻塞,等待锁的释放
- WAITING 等待状态: 调用不带时间的 Object.wait, Thread.join LockSupport.park 之后
- TIMED_WAITING 指定时间的等待状态: 调用带有时间参数的 Thread.sleep, Object.wait, Thread.join, LockSupport.parkNanos,LockSupport.parkUntil等方法之后
- TERMINATED 死亡状态: 运行结束或者异常终止的状态
开启一个线程
Thread以Runnable对象作为构造参数,重写run 实现Runnable接口的类对象可以作为构造参数传递给Thread子类的构造方法
// 子类或匿名内部类重写 run 方法
Thread thread = new Thread() {
@Override
public void run() {
super.run();
Log.i(TAG, "thread run");
}
};
thread.start();
// 传入 Runnable 对象作为构造参数
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "thread run 2");
}
});
thread.start();
// 同时传入参数和重写 run 方法, 使用 Runnable
Thread 的方法
- .currentThread() 静态方法, 获取当前线程
- .getName()
- .getID()
- .sleep()
- .isAlive() 线程是否存活(可运行或被阻塞时)
- .yield() 放弃当前CPU资源,等待下次分配
- .getState() 获取线程状态
多线程同步
- synchronized 独占, 多个线程不可同时操作该数据,线程数据安全, 注意独占会引发死锁
- volatile 保证数据最新,可同时操作, 保证从公共堆栈中取值
相关异常
- 线程sleep时调用interrupt方法,会抛出异常
- stop方法调用时,可能会抛出ThreadDeath异常,不必要显示捕捉,可显示捕捉写处理代码
- 在子线程中的变量变化了, 可能是在主线程或其他线程中被操作了, 子线程中的变量要注意同步
停止线程
线程运行结束或异常则停止
一:</br> interrupt() 方法, 在某个地方打完标记, 然后run里的循环()的地方判断下状态, 打过标记的就退出</br>
- .interrupt() 打中断标记
- .interrupted() 是否是中断状态, 并清除中断标记
- .isInterrupted() 是否是中断状态, 不清除中断标记</br>
如果判断是中断状态,则使用return或者抛出异常来停止线程</br>
二:</br> stop()方法
暂停线程
- suspend()和resume() 方法已过时
- suspend方法暂停线程的执行(run方法暂停),resume恢复执行
- 暂停之后的代码在恢复之前不会执行,可能带来数据不同步的问题
- 如果在线程在执行到一个synchronized代码块时暂停,则这一个代码块不会被释放(注意一些官方的方法使用了synchronized)
优先级
- .setPriority() 设置优先级(1~10)
- 继承性: 优先级子线程会继承父线程的优先级
- 优先级高的获得的资源多, 优先执行完的概率大, 但优先级低的也有可能比高的先执行完
线程安全
- 多个线程对同一个对象的某个属性操作, 可能会有数据同步问题
- 多个线程读同一个变量, 可能出现"脏读"
解决:
- 在方法签名前加上synchronized, 当一个线程操作一个被synchronized修饰的方法时,该对象的所有synchronized修饰的方法都独占, 没有被修饰的方法其他线程可访问
- 将一整个方法使用synchronized修饰, 因为方法内有一些不需要同步的代码, 会影响性能, 这事可以使用同步代码块, 同一个对象里的同步代码块同时只能有一个线程访问
其它
- synchronized修饰的方法或代码块只能有一个线程同时访问
- 代码块的对象监视器可以为非this的对象, 对象监视器为同一个对象时, 访问该代码块, 其它对象监视器为该对象的代码块无法访问, 代码块使用的对象监视器的对象不变, 属性可以改变
- 使用synchronized来限制一个static方法, 将会锁住整个类, 所有该类的对象的访问都会受到限制
等待/通知机制
- 每一个对象都有wait()/notify()方法
- wait()使当前线程释放锁, 从运行状态退出, 进入等待状态
- notify()随机唤醒一个使用同一共享资源的线程从等待状态退出, 没有等待的就忽视, 被唤醒的线程需要等待当前调用notify()方法的线程执行完之后才有可能继续执行, 同时获取到锁, 原因是wait()/notify()是同一个对象监听器
- notifyAll() 唤醒全部
- 给一个处于wait()状态的线程打中断标记(interrupt), 这个线程会出异常
- wait(long) 等待一段时间, 如果没有线程来唤醒, 就自己进入唤醒状态
join方法
- join()方法使当前的调用线程的接下来代码在被调用线程结束之后开始执行
- 相当于一个wait()方法, 同样interrupt()方法会使调用线程报错
ThreadLocal类使用
- 存储每个线程的私有数据
- 同一个实例, 不同的线程存入数据, 取出时他的数据也是不同的, 是被分隔的, 互不影响.
- 类的实例调用get方法获取数据, 没数据时返回null(可重写* initialValue()方法设置该返回值), 通过set方法存入数据 InheritableThreadLocal类可以子线程中获取到父线程存入的值, 其中重写该类中的childValue方法可以设置子线程调用get方法后获取的数据;
Lock的使用
ReentrantLock的实例 .lock() .unlock() 获取锁和释放锁</br> ReentrantLock的实例调用 .newCondition()方法获取Condition实例,</br> Condition实例的await(), signal(), signalAll()实现等待通知, 在调用await()方法前要先获取锁</br> Condition实例可以多个, 在多个线程时可以更好的调用
ReentrantLock类
构造参数选用的bool值可以构造出公平锁或非公平锁 公平锁, 线程先进先执行</br>
方法:
- getHoldCount() 当前线程保持锁定的个数, 即调用lock()的次数
- getQueueLength() 正在等待获取线程锁的线程个数
- getWaitQueueLength(Condition) 返回与此此锁定Condition相关的线程估计数
- hasQueuedThread(Thread) 指定的线程是否在等待获取此锁定
- hasQueuedThreads() 是否有线程在等待获取此锁定
- hasWaiters(Condition) 查询是否有线程在等待与此锁定相关的Condition条件
- isFair() 判断是否是公平锁
- isHeldByCurrentThread() 查询当前线程是否保持此锁定
- isLocked() 查询此锁定是否有任意线程保持
- lockInterruptibly() 当前线程未中断, 获取锁定; 已经被中断则抛出异常
- tryLock() 在锁未被另一个线程保持的情况下获取锁定
- tryLock(long,TimeUnit) 如果锁在一定时间内没有被另一个线程获取 , 且当前线程未中断, 则获取锁
ReentrantReadWriteLock类
ReentrantLock是完全互斥, ReentrantReadWriteLock的两个读锁不是互斥的, 读写锁、写读锁、写写琐是互斥的, 互斥的同时只能有一个线程执行。</br> ReentrantReadWriteLock的锁在读的多个线程可同时执行</br> 读锁(共享锁):lock.readLock().lock()</br> 写锁(排他锁):lock.writeLock().lock()
线程组ThreadGroup类
ThreadGroup类对象调用方法对线程组内的线程都适用
异常
给线程设置默认的异常处理, 线程对象调用setUncaughtExceptionHandler()</br> 自定义ThreadGroup类可以重写uncaughtException方法, 在组内的某个线程报错时回调
例子 </br> 同步代码块(对象监视器为this)
synchronized (this ) {
//代码
}